package org.beetl.json;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import org.beetl.json.util.ContextLocal;
import org.beetl.json.util.IntIOWriter;

public class JsonWriter {
	Writer w = null ;
	char quotes = 0;
	static char[] NULL = "null".toCharArray();
	boolean pretty = false ;
	int indent = 0;
	LinkedList<Object> objectPath = new LinkedList<Object>();
	HashMap<Class,List<Location>> classTypePolicy = new HashMap<Class,List<Location>>();
	HashMap<Class,List<Location>> inHeritedClassTypePolicy = new HashMap<Class,List<Location>>();
	ContextLocal localBuffer = ContextLocal.get();
	private boolean remberObject = false ;
	public JsonWriter(Writer w){
		this.w = w;
		if(JsonTool.singleQuotes){
			quotes = '\'';
		}else{
			quotes= '\"';
		}
		pretty = JsonTool.pretty;
		
	}
	
	
	
	public void writeComma() throws IOException{
		w.write(',');
	
	}
	
	
	
	public void writeScopeChar(char c) throws IOException{
		if(pretty){			
			switch(c){
			case '{':
			case '[':
				w.write('\n');
				this.writeIndent();				
				this.indent++;
				break;
			case '}':
			case ']':
				w.write('\n');
				this.indent--;
				writeIndent();				
				break;			
			}
			
			
		}
		
		
		w.write(c);
	}
	public void  write(char c) throws IOException{
		
		
		w.write(c);
	}
	

	public void writeKey(String key)throws IOException{
		if(this.pretty){
			w.write('\n');
			writeIndent();
		}
		w.write(quotes);
		w.write(key);
		w.write(quotes);
	}
	
	public void writeKeyChars(char[] cs)throws IOException{
		w.write(cs);
	}
	

	public void writeValue(Object  o) throws IOException{
		if(o==null){
			w.write(NULL);			
		}else{
			if(o instanceof Boolean){
				w.write(o.toString());
			}else if(o instanceof Number){
				
				if(o instanceof Integer){
					IntIOWriter.writeInteger(this, (Integer)o);
				}else{
					w.write(o.toString());
				}
				
			}else if(o instanceof DirectOutputValue){
				w.write(o.toString());
			}else{
				w.write(quotes);
				w.write(o.toString());
				w.write(quotes);
			}
		
		}
		
	}
	
	public void writeNumberChars(char[] chars, int len) throws IOException

	{
		this.w.write(chars, 0, len);

	}
	
	protected void writeIndent() throws IOException{
		for(int i=0;i<indent;i++){
			w.write('\t');
		}
	}
	
	public void addObject(Object o){
		this.objectPath.add(o);
	}
	
	public boolean containObject(Object o){
		return objectPath.contains(o);
	}
	
	public void removeLast(){
		objectPath.removeLast();
	}

	public ContextLocal getLocalBuffer() {
		return localBuffer;
	}
	
	public void addClassTypeLoaction(Class cls,Location location){
		List<Location> list = this.classTypePolicy.get(cls);
		if(list==null){
			list = new ArrayList<Location>();
			classTypePolicy.put(cls, list);
		}
		list.add(location);
	}
	
	
	public void addInHeritedClassTypeLoaction(Class cls,Location location){
		List<Location> list = this.inHeritedClassTypePolicy.get(cls);
		if(list==null){
			list = new ArrayList<Location>();
			inHeritedClassTypePolicy.put(cls, list);
		}
		list.add(location);
	}

	public List<Location> getClassTypeLoaction(Class c){
		List<Location> all = new ArrayList<Location>();
		List<Location> list = this.classTypePolicy.get(c);
		if(list!=null)all.addAll(list);
		if(this.inHeritedClassTypePolicy.size()!=0){
			for(Entry<Class,List<Location>> entry:inHeritedClassTypePolicy.entrySet()){
				if(entry.getKey().isAssignableFrom(c)){
					all.addAll(entry.getValue());
				}
			}
		}
		return list;
		
	}



	public boolean isRemberObject() {
		return remberObject;
	}



	public void setRemberObject(boolean remberObject) {
		this.remberObject = remberObject;
	}



}
